package org.nmgyjt.springboot.utils;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @description: redis工具类
 */
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate redisTemplate;



    /**
     * Remove the specified keys. If a given key does not exist no operation is
     * performed for this key.
     *
     * return false if one of the key is not exist.
     */
    public Long del(final String... keys) {
        return redisTemplate.delete(Arrays.asList(keys));
    }
    /**
     * 删除对应的value
     *
     * @param key
     */
    public Boolean remove(final String key) {
        if (exists(key)) {
            return redisTemplate.delete(key);
        }
        return Boolean.TRUE;
    }

    // / String Actions ///

    /**
     * Get the value of the specified key. If the key does not exist null is
     * returned. If the value stored at key is not a string an error is returned
     * because GET can only handle string values.
     */
    public String get(final String key) {
        Object result = redisTemplate.opsForValue().get(key);
        return null == result ? null : result.toString();
    }

    public <T>T get(final String key, Class<T> type) {
        return  JSONObject.parseObject(get(key), type);
    }

    /**
     * Get the value of the specified key as Long.If the key does not exist null is returned.
     */
    public Long getAsLong(final String key) {
        String result = get(key);
        return result != null ? Long.valueOf(result) : null;
    }

    /**
     * Get the value of the specified key as Integer.If the key does not exist null is returned.
     */
    public Integer getAsInt(final String key) {
        String result = get(key);
        return result != null ? Integer.valueOf(result) : null;
    }
    public Map<String, Object> getMapValue(String mapName) {
        HashOperations<String, String, Object> hps = redisTemplate.opsForHash();
        return hps.entries(mapName);
    }


    /**
     * Get the values of all the specified keys. If one or more keys dont exist
     * or is not of type String, a 'nil' value is returned instead of the value
     * of the specified key, but the operation never fails.
     */
    public List<String> mget(final String... keys) {
        return redisTemplate.opsForValue().multiGet(Arrays.asList(keys));

    }

    /**
     * Set the string value as value of the key.
     * The string can't be longer than 1073741824 bytes (1 GB).
     */
    public void set(final String key, final String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public void set(final String key, final Object value) {
        set(key, JSONObject.toJSONString(value));
    }
    /**
     * The command is exactly equivalent to the following group of commands:
     * The operation is atomic.
     */
    public void setex(final String key, final String value, final int seconds) {
        redisTemplate.opsForValue().set(key,value,seconds, TimeUnit.SECONDS);
    }

    /**
     * The command is exactly equivalent to the following group of commands:
     * The operation is atomic.
     */
    public void setex(final String key, final Object value, final int seconds) {
        redisTemplate.opsForValue().set(key,JSONObject.toJSONString(value),seconds, TimeUnit.SECONDS);
    }

    /**
     * SETNX works exactly like
     * difference that if the key already exists no operation is performed.
     * SETNX actually means "SET if Not eXists".
     *
     * return true if the key was set.
     */
    public Boolean setnx(final String key, final String value) {
        return redisTemplate.opsForValue().setIfAbsent(key,value);
    }

    /**
     * The command is exactly equivalent to the following group of commands: {@link #setex(String, String, int) SETEX} +
     *
     * The operation is atomic.
     */
    public Boolean setnxex(final String key, final String value, final int seconds) {
        return redisTemplate.opsForValue().setIfAbsent(key,value,seconds,TimeUnit.SECONDS);
    }

    /**
     * GETSET is an atomic set this value and return the old value command. Set
     * key to the string value and return the old value stored at key. The
     * string can't be longer than 1073741824 bytes (1 GB).
     */
    public String getSet(final String key, final String value) {
        Object result =  redisTemplate.opsForValue().getAndSet(key,value);
        return null == result ? null : result.toString();
    }

    /**
     * Increment the number stored at key by one. If the key does not exist or
     * contains a value of a wrong type, set the key to the value of "0" before
     * to perform the increment operation.
     * <p>
     * INCR commands are limited to 64 bit signed integers.
     * <p>
     * Note: this is actually a string operation, that is, in Redis there are not "integer" types. Simply the string
     * stored at the key is parsed as a base 10 64 bit signed integer, incremented, and then converted back as a string.
     *
     * @return Integer reply, this commands will reply with the new value of key
     *         after the increment.
     */

    public Long incrBy(final String key, final long increment) {
        return redisTemplate.opsForValue().increment(key,increment);
    }

    public Double incrByFloat(final String key, final double increment) {
        return  redisTemplate.opsForValue().increment(key,increment);
    }


    // / Hash Actions ///
    /**
     * If key holds a hash, retrieve the value associated to the specified
     * field.
     * <p>
     * If the field is not found or the key does not exist, a special 'nil' value is returned.
     */
    public String hget(final String key, final String fieldName) {
        Object result = redisTemplate.opsForHash().get(key,fieldName);
        return null == result ? null : result.toString();
    }

    public List<String> hmget(final String key, final String... fieldsNames) {
        return redisTemplate.opsForHash().multiGet(key,Arrays.asList(fieldsNames));
    }

    public Map<String, String> hgetAll(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    public void hset(final String key, final String fieldName, final String value) {
        redisTemplate.opsForHash().put(key,fieldName,value);
    }

    public void hmset(final String key, final Map<String, String> map) {
        redisTemplate.opsForHash().putAll(key,map);

    }

    public Boolean hsetnx(final String key, final String fieldName, final String value) {
        return redisTemplate.opsForHash().putIfAbsent(key,fieldName,value);
    }

    public Long hincrBy(final String key, final String fieldName, final long increment) {
        return redisTemplate.opsForHash().increment(key,fieldName,increment);
    }

    public Double hincrByFloat(final String key, final String fieldName, final double increment) {
        return redisTemplate.opsForHash().increment(key,fieldName,increment);
    }

    public Long hdel(final String key, final String... fieldsNames) {
        return redisTemplate.opsForHash().delete(key,fieldsNames);
    }

    public Boolean hexists(final String key, final String fieldName) {
        return redisTemplate.opsForHash().hasKey(key,fieldName);

    }

    public Boolean exists( String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     * @param key
     * @param timeOut 秒
     * @return
     */
    public Boolean expire( String key,long timeOut) {
        return redisTemplate.expire(key,timeOut,TimeUnit.SECONDS);
    }

    public Set<String> hkeys(final String key) {
        return  redisTemplate.opsForHash().keys(key);
    }

    public Long hlen(final String key) {
        if (exists(key)){
            return Long.valueOf(redisTemplate.opsForHash().keys(key).size());
        }
        return 0L;

    }

    // / List Actions ///

    public Long lpush(final String key, final String... values) {
        return  redisTemplate.opsForList().leftPushAll(key,values);
    }

    public Long rpush(final String key, final String... values) {
        return  redisTemplate.opsForList().rightPushAll(key,values);
    }

    public String rpop(final String key) {
        Object result =  redisTemplate.opsForList().rightPop(key);
        return null == result ? null : result.toString();
    }

    public String lpop(final String key) {
        Object result =  redisTemplate.opsForList().leftPop(key);
        return null == result ? null : result.toString();
    }

    public String brpop(final String key) {
        return brpop(0,key);
    }

    public String brpop(final int timeout, final String key) {
        Object result= redisTemplate.opsForList().rightPop(key,timeout,TimeUnit.SECONDS);
        return null == result ? null : result.toString();
    }

    /**
     * Not support for sharding.
     */
    public String rpoplpush(final String sourceKey, final String destinationKey) {
        Object result=  redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,destinationKey);
        return null == result ? null : result.toString();
    }

    /**
     * Not support for sharding.
     */
    public String brpoplpush(final String source, final String destination, final int timeout) {
        Object result=  redisTemplate.opsForList().rightPopAndLeftPush(source,destination,timeout,TimeUnit.SECONDS);
        return null == result ? null : result.toString();
    }

    public Long llen(final String key) {
        return redisTemplate.opsForList().size(key);
    }

    public String lindex(final String key, final long index) {
        Object result=  redisTemplate.opsForList().index(key,index);
        return null == result ? null : result.toString();
    }

    public Long linsertAfter(final String key, final String pivot,
                             final String value) {
        return redisTemplate.opsForList().rightPush(key,pivot,value);
    }
    public Long linsertBefore(final String key, final String pivot,
                              final String value) {
        return redisTemplate.opsForList().leftPush(key,pivot,value);
    }
    public List<String> lrange(final String key, final int start, final int end) {
        return redisTemplate.opsForList().range(key,start,end);
    }

    public void ltrim(final String key, final int start, final int end) {
        redisTemplate.opsForList().trim(key,start,end);
    }


    public void ltrimFromLeft(final String key, final int size) {
        ltrim(key,0,size-1);
    }

    /**
     * 通过索引来设置元素的值。
     * @param key
     * @param index
     * @param value
     * @return
     */
    public void lset(final String key,final long index,final String value) {
        redisTemplate.opsForList().set(key,index,value);
    }

    public Boolean lremFirst(final String key, final String value) {
        return lremove(key,1,value);
    }
    public Boolean lremove(final String key, final long count, final String value){
        long l = redisTemplate.opsForList().remove(key,count,value);
        return l>0?Boolean.TRUE:Boolean.FALSE;
    }
    public Boolean lremAll(final String key, final String value) {
        return lremove(key,0,value);
    }

    // / Set Actions ///
    public Boolean sadd(final String key, final String member) {
        return redisTemplate.opsForSet().add(key,member)==1? true : false;
    }

    // / Set Actions ///
    public Boolean srem(final String key, final String member) {
        return redisTemplate.opsForSet().remove(key,member) == 1 ? true : false;
    }

    public Set<String> smembers(final String key) {
        return redisTemplate.opsForSet().members(key);
    }


    // / Ordered Set Actions ///
    /**
     * return true for add new element, false for only update the score.
     */
    public Boolean zadd(final String key, final double score, final String member) {
        return  redisTemplate.opsForZSet().add(key,member,score);
    }

    public Double zscore(final String key, final String member) {
        return redisTemplate.opsForZSet().score(key,member);
    }

    public Long zrank(final String key, final String member) {
        return redisTemplate.opsForZSet().rank(key,member);
    }

    public Long zrevrank(final String key, final String member) {
        return redisTemplate.opsForZSet().reverseRank(key,member);
    }

    public Long zcount(final String key, final double min, final double max) {
        return redisTemplate.opsForZSet().count(key,min,max);
    }

    public Set<String> zrange(final String key, final int start, final int end) {
        return redisTemplate.opsForZSet().rangeByScore(key,start,end);
    }

    public Set<RedisZSetCommands.Tuple> zrangeWithScores(final String key, final int start, final int end) {
        return redisTemplate.opsForZSet().rangeWithScores(key,start,end);
    }

    public Set<String> zrevrange(final String key, final int start, final int end) {
        return redisTemplate.opsForZSet().reverseRange(key,start,end);

    }

    public Set<RedisZSetCommands.Tuple> zrevrangeWithScores(final String key, final int start, final int end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key,start,end);
    }

    public Set<String> zrangeByScore(final String key, final double min, final double max) {
        return redisTemplate.opsForZSet().rangeByScore(key,min,max);

    }

    public Set<RedisZSetCommands.Tuple> zrangeByScoreWithScores(final String key, final double min, final double max) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key,min,max);

    }

    public Set<String> zrevrangeByScore(final String key, final double max, final double min) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key,min,max);
    }

    public Set<RedisZSetCommands.Tuple> zrevrangeByScoreWithScores(final String key, final double max, final double min) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,min,max);

    }

    public Boolean zrem(final String key, final String member) {
        return redisTemplate.opsForZSet().remove(key,member) == 1 ? true : false;
    }

    public Long zremByScore(final String key, final double start, final double end) {
        return redisTemplate.opsForZSet().removeRangeByScore(key,start,end);

    }

    public Long zremByRank(final String key, final long start, final long end) {
        return redisTemplate.opsForZSet().removeRange(key,start,end);
    }

    public Long zcard(final String key) {
        return  redisTemplate.opsForZSet().zCard(key);
    }

    public boolean preventManyCommit(String key, String value, Long expireTime) {
        if(get(key) == null){
            try {
                ValueOperations<String, Object> operations = redisTemplate.opsForValue();
                operations.set(key, value);
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }else{
            return false;
        }
    }
}
